package fr.cephb.berkeleydb.dom;

import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;

import org.w3c.dom.DOMException;

import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Transaction;

import fr.cephb.berkeleydb.db.PrimaryDB;

public abstract class Node
	{
	public static final short ELEMENT_NODE=1;
	public static final short TEXT_NODE=3;
	public static final short DOCUMENT_NODE=9;
	public static final short COMMENT_NODE=org.w3c.dom.Node.COMMENT_NODE;
	public static final short CDATA_SECTION_NODE=org.w3c.dom.Node.CDATA_SECTION_NODE;
	
	protected long parent_id=-1L;
	protected long id=-1L;
	protected long first_child_id;
	protected long prev_sibling_id=-1;
	protected long next_sibling_id=-1;
	private Document owner;
	
		
	public abstract int getNodeType();
	
	protected Node(long id, Document owner)
		{
		this.id=id;
		this.owner=owner;
		}
	
	public Document getOwnerDocument()
		{
		return this.owner;
		}
	
	public long getId()
		{
		return id;
		}
	
	protected Node getNodeById(final long node_id) throws DatabaseException
		{
		if(node_id==this.id) return this;
		return getOwnerDocument().getNodeById(node_id);
		}
	
	protected PrimaryDB<Long,Node> getDatabase()
		{
		return getOwnerDocument().getDatabase();
		}
	
	protected Transaction getTransaction()
		{
		return  getOwnerDocument().getTransaction();
		}
	
	protected void synch() throws DatabaseException
		{
		getOwnerDocument().synch(this);
		}
	
	public Node getFirstChild()
		{
		return getNodeById(first_child_id);
		}
	
	public Node getLastChild()
		{
		Node curr= getFirstChild();
		if(curr==null) return null;
		while(curr.next_sibling_id!=-1);
			{
			curr=curr.getNextSibling();
			assert(curr!=null);
			}
		return curr;
		}
	
	public Node getParentNode()
		{
		return getNodeById(parent_id);
		}
	
	public Node getNextSibling()
		throws DatabaseException
		{
		return getOwnerDocument().getNodeById(next_sibling_id);
		}
	
	public Node getPrevSibling()
	throws DatabaseException
		{
		return getOwnerDocument().getNodeById(prev_sibling_id);
		}
	
	public void unlink()
		throws DatabaseException
		{
		Node prev= getPrevSibling();
		Node next= getNextSibling();
		Node parent = getParentNode();
		
		if(prev!=null)
			{
			assert(prev.next_sibling_id==this.id);
			prev.next_sibling_id=(next==null?-1L:next.id);
			prev.synch();
			}
		if(next!=null)
			{
			assert(next.prev_sibling_id==this.id);
			next.prev_sibling_id=(prev==null?-1L:prev.id);
			next.synch();
			}
		if(parent!=null)
			{
			if(parent.first_child_id==this.id)
				{
				parent.first_child_id=(next==null?-1L:next.id);
				parent.synch();
				}
			}
		this.parent_id=-1L;
		this.prev_sibling_id=-1L;
		this.next_sibling_id=-1L;
		this.synch();
		}
	
	public Node appendChild(Node n)
		throws DatabaseException
		{
		if(n==null) return null;
		if(n==this) throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR,"Cannot add child to itself");
		if(n.getOwnerDocument()!=getOwnerDocument())
			throw new DOMException(DOMException.WRONG_DOCUMENT_ERR,"Wront document");
		n.unlink();
		
		n.parent_id=this.id;
		if(this.first_child_id==-1L)
			{
			this.first_child_id=n.id;
			this.synch();
			}
		else
			{
			Node curr= getFirstChild();
			assert(curr!=null);
			while(curr.next_sibling_id!=-1);
				{
				curr=curr.getNextSibling();
				assert(curr!=null);
				}
			curr.next_sibling_id=n.id;
			n.prev_sibling_id=curr.id;
			curr.synch();
			}
		n.synch();
		return n;
		}
	
	public abstract void write(XMLStreamWriter writer) throws XMLStreamException;
	
	public String getTextContent()
		{
		StringBuilder b=new StringBuilder();
		textContent(b);
		return b.toString();
		}
	
	private void textContent(StringBuilder b)
		{
		switch(getNodeType())
			{
			case TEXT_NODE:
				{
				b.append(Text.class.cast(this).getText());
				break;
				}
			case DOCUMENT_NODE:
			case ELEMENT_NODE:
				{
				Node curr=getFirstChild();
				while(curr!=null)
					{
					curr.textContent(b);
					curr=curr.getNextSibling();
					}
				break;
				}
			}
		}

	@Override
	public int hashCode()
		{
		final int prime = 31;
		int result = 1;
		result = prime * result + (int) (id ^ (id >>> 32));
		return result;
		}

	@Override
	public boolean equals(Object obj)
		{
		if (this == obj) return true;
		if (obj == null) return false;
		if (getClass() != obj.getClass()) return false;
		Node other = (Node) obj;
		if (id != other.id) return false;
		if(owner!=other.owner) return false;
		return true;
		}
	
	
	}
